/************************************************************************
 * @file PluginDescription.h
 * @author Ian Chen
 * @version 1.0
 *
 * @section LICENSE
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 3 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser
 * General Public License for more details at
 * http://www.gnu.org/licenses/lgpl.html
 *
 ************************************************************************/
#ifndef PLUGINDESCRIPTION_H
#define PLUGINDESCRIPTION_H

#include <QString>
#include <QStringList>
#include <vector>
#include <map>
#include <memory>

#include <plugin/plugin_global.h>
#include <plugin/plugin/PluginRequirement.h>

using namespace std;

namespace antu
{
namespace plugin
{
class AbstractPlugin;

typedef void (*pFactoryInitFunc)(const QString& code);
#define FACTORY_INIT_FUNC "InitFactory"

// descriptions are managed by FrameworkPlugin
class PLUGINSHARED_EXPORT PluginDescription
{
private:

    // name of the plugin
    QString m_sName;

    // current version of the plugin
    PluginVersion m_oVersion;

    // minimum compatible version
    PluginVersion m_oMinimumCompatibleVersion;

    // plugin dependencies
    vector<PluginRequirement> m_vRequiredPlugins;

    // plugin path
    QString m_sPath;

    QString m_sCode;

    // start level
    int		m_iStartLevel;

    // plugin application groups
    QStringList m_oApplicationGroups;

    QString m_sLibName;

public:
    //!
    /*!
     */
    PluginDescription(const QString& name, const PluginVersion& version,
        const PluginVersion& minimumCompatibleVersion);

    //!
    /*!
     */
    PluginDescription(const QString& name, const QString& versionString,
        const QString& minimumCompatibleVersion);

    //!
    /*!
     */
    ~PluginDescription();

    //! Name of the plugin.
    inline QString name() const;

    inline QString code() const
    {
        return this->m_sCode;
    }

    inline void setCode(const QString& code)
    {
        this->m_sCode = code;
    }

    //! Version of the plugin.
    inline PluginVersion version() const;

    //! Minimum compatible version of the plugin.
    inline PluginVersion minimumCompatibleVersion() const;

    //! Execution path of the plugin.
    /*! Execution path of the plugin.
     */
    inline QString	path() const;

    //! Sets the execution path of the plugin.
    /*! Sets the execution path of the plugin.
     */
    inline void		setPath(const QString& path);

    /*! Plug-ins are loaded by dependency order. I.e. if plugin B
        needs services provided by plugin A, then plugin A is loaded
        before B, and A is said to have a higher load order, and B
        is said to have a lower load order. If plug-ins
        B and C both require service from plugin A, and A only, then
        plug-ins B and C are of the same load order. Plug-ins that are
        of the same load order are loaded in order of start level. Plug-ins
        that have a smaller start level value will be loaded first.
      \return Start level of plugin.
      \sa setStartLevel()
     */
    inline int		startLevel() const;

    //! Sets the start level of the plugin.
    /*! Sets the start level of the plugin.
     */
    inline void		setStartLevel(int startLevel);

    //! Checks if an application group is supported by this plugin.
    /*! Checks to see if a group name is within the supported group names
        of the plugin. if a framework's group name is not within the supported
        groups, and the plugin is not a generic plugin ie a plugin that is loaded
        regardless of the group name, then the plugin will not be loaded

       \return returns true if the group name is supported, false otherwise.
     */
    inline bool		isSupportedApplicationGroup(const QString& groupName) const;

    bool isSupportedApplicationGroup(const QStringList& groups) const;

    //! Adds a group name to the supported group name list.
    /*! Adds a group name to the supported group name list.

      \param groupName Group name that should be supported.
     */
    inline void		addApplicationGroup(const QString& groupName);

    //! Adds a required plugin.
    /*! Adds a required plugin.
     */
    inline void    addDependency(const PluginRequirement& requirement);

    //! Retrieves PluginRequirement at given position.
    /*! A plugin may be dependent upon one or more other plug-ins. This method
        allows one to iterate through a plugin's required plug-ins.

      \param pos Position of requirement to retrieve.
      \return Returns PluginRequirement, throws a string if index is out of range.
     */
    PluginRequirement requirementAt(size_t pos) const;

    //! Retrieves number of requirements.
    /*! Retrieves number of requirements.

      \return Returns the number of required plugins.
     */
    size_t requirementCount() const;

    size_t applicationGroupCount() const
    {
        return this->m_oApplicationGroups.size();
    }

    QString applicationGroupAt(size_t i) const
    {
        Q_ASSERT(i >= 0 && i < this->applicationGroupCount());
        return this->m_oApplicationGroups[i];
    }

    bool operator==(const PluginDescription& description) const;

    bool isRequiredPlugin(const QString& code) const;

    inline void setLibName(const QString& libName)
    {
        this->m_sLibName = libName;
    }

    inline QString libName() const
    {
        if (this->m_sLibName.isEmpty())
            return this->code();
        else
            return this->m_sLibName;
    }

    //friend class AbstractPlugin;

public:
    //! Name of application group that if specified, allows a plugin to be loaded regardless of the actual application group of the application.
    /*!
     */
    const static QString UNIVERSAL_APP_GROUP;
};

typedef shared_ptr<PluginDescription> PluginDescriptionSharedPtr;
}
}

#endif // PLUGINDESCRIPTION_H
